package utils

import (
	"context"
	"database/sql"
	"fmt"
	"gitee.com/chunanyong/zorm"
	"github.com/gogf/gf/v2/util/gconv"
	"github.com/zeromicro/go-zero/core/logx"
	"github.com/zeromicro/go-zero/core/threading"
	"mzpd_server/internal/config"
	"mzpd_server/internal/consts"
	"mzpd_server/internal/svc"
	"mzpd_server/internal/types"
	"mzpd_server/models/player_queues"
	"sync"
	"time"
)

// ServiceUtils 接收his推送消息
type ServiceUtils struct {
	methods    map[string]FuncMsg
	mySqlDbDao *zorm.DBDao     `json:"mySqlDbDao"`
	mysqlCtx   context.Context `json:"mysqlCtx"`
	config     config.Config
	dbInit     bool
	serviceCtx *svc.ServiceContext
}

var serviceInstance *ServiceUtils
var serviceOnce sync.Once

type FuncMsg func(value interface{})

func GetServiceUtils() *ServiceUtils {
	serviceOnce.Do(func() {
		serviceInstance = &ServiceUtils{
			methods: make(map[string]FuncMsg),
			dbInit:  false,
		}
	})
	return serviceInstance
}

func (s *ServiceUtils) Start(config config.Config, serviceCtx *svc.ServiceContext) {
	s.config = config
	s.serviceCtx = serviceCtx

	//s.methods[consts.METHOD_EQ00113] = s.roomLogic
	s.methods[consts.METHOD_EQA00113] = s.doctorCallLogic
	s.methods[consts.METHOD_EQ00114] = s.doctorInvakeLogic
	s.methods[consts.METHOD_EQ00115] = s.deptQueueLogic

	for !s.dbInit {
		s.dbInit = s.initDB()
	}
	fmt.Printf("启动成功")
}

// initDB 初始化数据库连接
func (s *ServiceUtils) initDB() bool {
	config := s.config
	dsn := config.DNS
	driveName := config.DriverName
	dialect := config.Dialect
	var dbConfig = &zorm.DataSourceConfig{
		//DSN 数据库的连接字符串,parseTime=true会自动转换为time格式,默认查询出来的是[]byte数组.&loc=Local用于设置时区
		DSN: dsn,
		//DSN: "oracle://zlhis:his@10.10.10.99:1521/orcl",
		//sql.Open(DriverName,DSN) DriverName就是驱动的sql.Open第一个字符串参数,根据驱动实际情况获取
		DriverName:            driveName, //数据库驱动名称
		Dialect:               dialect,   //数据库类型
		SlowSQLMillis:         0,         //慢sql的时间阈值,单位毫秒.小于0是禁用SQL语句输出;等于0是只输出SQL语句,不计算执行时间;大于0是计算SQL执行时间,并且>=SlowSQLMillis值
		MaxOpenConns:          0,         //数据库最大连接数,默认50
		MaxIdleConns:          0,         //数据库最大空闲连接数,默认50
		ConnMaxLifetimeSecond: 0,         //连接存活秒时间. 默认600(10分钟)后连接被销毁重建.
		//避免数据库主动断开连接,造成死连接.MySQL默认wait_timeout 28800秒(8小时)
		DefaultTxOptions: nil, //事务隔离级别的默认配置,默认为nil
	}
	mySqlDbDao, err := zorm.NewDBDao(dbConfig)
	if err != nil {
		fmt.Errorf("数据库连接异常 %s", err.Error())
		logx.Errorf("数据库连接异常 %s", err.Error())
		return false
	}
	mysqlCtx, err := mySqlDbDao.BindContextDBConnection(context.TODO())
	if err != nil {
		logx.Errorf("数据库连接异常 %s", err.Error())
		return false
	}
	s.mysqlCtx = mysqlCtx
	s.mySqlDbDao = mySqlDbDao
	return true
}

// Request 处理请求
func (s *ServiceUtils) Request(method string, value interface{}) {
	var m = method
	var v = value
	var methodFunc = s.methods[m]
	if methodFunc != nil {
		threading.GoSafe(func() {
			methodFunc(v)
		})
	}
	//logx.Debugf("【收到的数据】", value)
}

// RoomLogic 处理诊室队列信息
func (s *ServiceUtils) roomLogic(value interface{}) {
	//fmt.Printf("%s", value)
}

// DoctorCallLogic 处理呼叫信息
func (s *ServiceUtils) doctorCallLogic(value interface{}) {
	var call DoctorCallPatientType
	err := gconv.Struct(value, &call)
	if err != nil {
		logx.Errorf("【EQA00113】医生呼叫转换出错了：%s,数据:%v", err.Error(), value)
		return
	}
	p := call.PatientList[0]
	registerId := s.getRegisterId(p.RegNo, p.RegisterId, gconv.String(p.PatientId), call.DeptId, call.RoomId)
	player, err := s.serviceCtx.PlayerQueuesModel.FindByPlayercode(context.Background(), sql.NullString{
		String: registerId,
		Valid:  true,
	})
	if err != nil {
		logx.Errorf("【EQA00113】医生呼叫获取患者信息失败:%s,数据:%v", err.Error(), value)
		return
	}
	if player == nil {
		logx.Errorf("【EQA00113】医生呼叫获取患者信息失败 player is null:%s,数据：%v", err.Error(), value)
		return
	}
	statusId := gconv.Int64(p.State)
	status := types.PlayerStatus{
		Status:     statusId,
		StatusName: consts.StatusMap[statusId],
		DeptId:     call.DeptId,
		DeptName:   call.DeptName,
		RoomId:     call.RoomId,
		RoomName:   call.RoomName,
		DoctorId:   call.DoctorId,
		DoctorName: call.DoctorName,
		CreateTime: time.Now().Format(consts.TIME_FORMAT),
	}
	player.State = sql.NullInt64{
		Int64: statusId,
		Valid: true,
	}
	player.Statelist = sql.NullString{
		String: gconv.String(status),
		Valid:  false,
	}
	player.Updatetime = sql.NullString{
		String: time.Now().Format(consts.TIME_FORMAT),
		Valid:  false,
	}
	err = s.serviceCtx.PlayerQueuesModel.Update(context.Background(), player)
	if err != nil {
		logx.Errorf("【EQA00113】医生呼叫更新患者状态失败:", err.Error())
		return
	}

	s.serviceCtx.Redis.Set("status_"+registerId, gconv.String(statusId))

	commonUtils := &CommonUtils{Ctx: context.Background(), serviceCtx: s.serviceCtx}
	commonUtils.PushStatusChanged(player.Groupid.Int64, player.Playercode.String)
}

// getRegisterId 获取患者挂号id,没有的就用患者id,科室,诊室来生成
func (s *ServiceUtils) getRegisterId(regNo string, regId string, args ...string) string {
	registerId := ""
	if regId != "" && regId != "0" && len(regId) > 1 {
		registerId = regId
	} else if regNo != "" && regNo != "0" && len(regNo) > 1 {
		registerId = regNo
	} else {
		for _, i2 := range args {
			registerId += i2 + "_"
		}
	}
	return registerId
}

// DoctorInvakeLogic 处理接诊信息
func (s *ServiceUtils) doctorInvakeLogic(value interface{}) {
	var call DoctorInvokePatientType
	err := gconv.Struct(value, &call)
	if err != nil {
		logx.Errorf("【EQ00114】医生接诊转换出错了：%s,%v", err.Error(), value)
		return
	}
	p := call
	registerId := s.getRegisterId(p.RegNo, p.RegisterId, p.PatientId, p.DeptId, p.RoomId)
	player, err := s.serviceCtx.PlayerQueuesModel.FindByPlayercode(context.Background(), sql.NullString{
		String: registerId,
		Valid:  true,
	})
	if err != nil {
		logx.Errorf("【EQ00114】医生接诊获取患者信息失败:%s,%v", err.Error(), value)
		return
	}
	if player == nil {
		logx.Errorf("【EQ00114】医生接诊获取患者信息失败 player is null:%v", value)
		return
	}
	var statusId int64 = 8
	status := types.PlayerStatus{
		Status:     statusId,
		StatusName: consts.StatusMap[statusId],
		DeptId:     call.DeptId,
		DeptName:   call.DeptName,
		RoomId:     call.RoomId,
		RoomName:   call.RoomName,
		DoctorId:   call.DoctorId,
		DoctorName: call.DoctorName,
		CreateTime: time.Now().Format(consts.TIME_FORMAT),
	}
	player.State = sql.NullInt64{
		Int64: statusId,
		Valid: true,
	}
	player.Statelist = sql.NullString{
		String: gconv.String(status),
		Valid:  false,
	}
	player.Updatetime = sql.NullString{
		String: time.Now().Format(consts.TIME_FORMAT),
		Valid:  false,
	}
	err = s.serviceCtx.PlayerQueuesModel.Update(context.Background(), player)
	if err != nil {
		logx.Errorf("【EQ00114】医生接诊更新患者状态失败:", err.Error())
		return
	}
	s.serviceCtx.Redis.Set("status_"+registerId, gconv.String(statusId))
	commonUtils := &CommonUtils{Ctx: context.Background(), serviceCtx: s.serviceCtx}
	commonUtils.PushStatusChanged(player.Groupid.Int64, player.Playercode.String)
}

// deptQueueLogic 科室队列信息
func (s *ServiceUtils) deptQueueLogic(value interface{}) {
	var deptModel types.DeptQueueModel
	err := gconv.Struct(value, &deptModel)
	if err != nil {
		logx.Errorf("【EQ00115】科室队列数据转换出错了,%s,%v", err.Error(), value)
		return
	}
	groupIds := make(map[int64]int64, 0)
	isUpdate := false
	var groupMap []map[string]interface{}
	R := s.serviceCtx.Redis
	for _, model := range deptModel.PatientList {
		model.DeptId = deptModel.DeptId
		model.DeptName = deptModel.DeptName
		//这里应该要在缓存里面查找,如果没找到才到数据库查
		registerId := s.getRegisterId("", model.RegisterId, model.PatientId, model.DeptId, model.RoomId)
		getStatus, err1 := R.Get("status_" + registerId)
		if err1 != nil {
			//fmt.Printf("Redis 获取状态出错:%s \n", err.Error())
		} else if getStatus != "" {
			if getStatus == model.State {
				//fmt.Printf("【EQ00115】状态一样,不用更新:%s  \n", model)
				continue
			}
		}
		playercode, err := s.serviceCtx.PlayerQueuesModel.FindByPlayercode(context.Background(), sql.NullString{
			String: registerId,
			Valid:  true,
		})
		if err != nil {
			logx.Errorf("【EQ00115】查询患者信息报错了： %s  \n", registerId, model.PatientId, model.PatientName, err.Error())
		}
		statusId := gconv.Int64(model.State)
		status := types.PlayerStatus{
			Status:     statusId,
			StatusName: consts.StatusMap[statusId],
			DeptId:     model.DeptId,
			DeptName:   model.DeptName,
			RoomId:     model.RoomId,
			RoomName:   model.RoomName,
			DoctorId:   model.DoctorId,
			DoctorName: model.DoctorName,
			CreateTime: time.Now().Format(consts.TIME_FORMAT),
		}
		if playercode == nil { //没有,插入
			if groupMap == nil {
				gIds, err := s.serviceCtx.GroupItemsModel.FindGroupItemsByDeptId(context.Background(), deptModel.DeptId)
				if err != nil {
					fmt.Printf("%s  \n", err.Error())
					logx.Errorf("【EQ00115】查询科室分组信息失败：%s, \n", err.Error())
					return
				}
				if gIds == nil || len(gIds) <= 0 {
					logx.Errorf("【EQ00115】没有找到当前科室的分组信息", deptModel.DeptId, deptModel.DeptName)
					return
				}
				groupMap = gconv.Maps(gIds)
			}
			groupid := s.getGroupId(model, groupMap)
			playercode = &player_queues.PlayerQueues{
				Id: 0,
				Playercode: sql.NullString{
					String: registerId,
					Valid:  true,
				},
				Groupid: sql.NullInt64{
					Int64: groupid,
					Valid: true,
				},
				Patientid: sql.NullString{
					String: model.PatientId,
					Valid:  true,
				},
				Patientname: sql.NullString{
					String: model.PatientName,
					Valid:  true,
				},
				Clinicnumber: sql.NullString{},
				Gender:       sql.NullString{},
				Age:          sql.NullString{},
				Birthday:     sql.NullString{},
				Cardno:       sql.NullString{},
				Seq: sql.NullString{
					String: model.Seq,
					Valid:  true,
				},
				Serialnumber: sql.NullString{
					String: model.Seq,
					Valid:  true,
				},
				Sort:  sql.NullFloat64{},
				Wiget: sql.NullString{},
				Deptid: sql.NullString{
					String: model.DeptId,
					Valid:  true,
				},
				Deptname: sql.NullString{
					String: model.DeptName,
					Valid:  true,
				},
				Roomid: sql.NullString{
					String: model.RoomId,
					Valid:  true,
				},
				Roomname: sql.NullString{
					String: model.RoomName,
					Valid:  true,
				},
				Doctorid: sql.NullString{
					String: model.DeptId,
					Valid:  true,
				},
				Doctorname: sql.NullString{
					String: model.DoctorName,
					Valid:  true,
				},
				Callcount: sql.NullInt64{},
				State: sql.NullInt64{
					Int64: statusId,
					Valid: true,
				},
				Statelist: sql.NullString{
					String: gconv.String(status),
					Valid:  true,
				},
				Registerway:     sql.NullString{},
				Ismake:          sql.NullString{},
				Isreclinicname:  sql.NullString{},
				Issreclinic:     sql.NullString{},
				Registertypeid:  sql.NullString{},
				Registertype:    sql.NullString{},
				Playeridentity:  sql.NullString{},
				Preengagemode:   sql.NullString{},
				Startseattime:   sql.NullString{},
				Endseattime:     sql.NullString{},
				Aheadatworktime: sql.NullString{},
				Looktime:        sql.NullString{},
				Createtime: sql.NullString{
					String: time.Now().Format(consts.TIME_FORMAT),
					Valid:  true,
				},
				Updatetime: sql.NullString{
					String: time.Now().Format(consts.TIME_FORMAT),
					Valid:  true,
				},
			}
			insert, err := s.serviceCtx.PlayerQueuesModel.Insert(context.Background(), playercode)
			if err != nil {
				logx.Errorf("添加患者信息失败： %s", playercode.Patientid, playercode.Patientname, err.Error())
			} else {
				affected, err := insert.RowsAffected()
				if err != nil {
					logx.Errorf("【EQ00115】受影响的行数错误： %s", playercode.Patientid, playercode.Patientname, err.Error())
				} else if affected <= 0 {
					logx.Errorf("【EQ00115】受影响的行数错误： %s", playercode.Patientid, playercode.Patientname, err.Error())
				} else {
					groupIds[groupid] = groupid
					isUpdate = true
				}
			}
		} else {
			if playercode.State.Int64 == gconv.Int64(model.State) {
				fmt.Printf("【EQ00115】状态一样的不用更新: %s,%s,%s,%s", model.RegisterId, model.PatientId, model.PatientName, err.Error())
				continue
			}
			playercode.State = sql.NullInt64{
				Int64: statusId,
				Valid: true,
			}
			playercode.Statelist = sql.NullString{
				String: gconv.String(status),
				Valid:  true,
			}
			err = s.serviceCtx.PlayerQueuesModel.Update(context.Background(), playercode)
			if err != nil {
				logx.Errorf("【EQ00115】更新患者失败: %s,%s \n", model.RegisterId, err.Error())
			} else {
				gId := playercode.Groupid.Int64
				groupIds[gId] = gId
				isUpdate = true
			}
		}
		if isUpdate {
			R.Set("status_"+registerId, gconv.String(statusId))
		}
	}
	if isUpdate {
		commonUtils := &CommonUtils{Ctx: context.Background(), serviceCtx: s.serviceCtx}
		commonUtils.PushQueues(groupIds)
	}
}

func (s *ServiceUtils) getGroupId(mod types.DeptUserModel, groupMap []map[string]interface{}) int64 {
	var groupid int64 = 0
	for _, v := range groupMap {
		isHave := false
		if mod.DeptId == v["deptid"] && mod.RoomId == v["roomid"] && mod.DoctorId == v["doctorid"] {
			isHave = true
		} else if mod.DeptId == v["deptid"] && mod.RoomId == v["roomid"] {
			isHave = true
		} else if mod.DeptId == v["deptid"] && mod.DoctorId == v["doctorid"] {
			isHave = true
		} else if mod.DeptId == v["deptid"] {
			isHave = true
		}
		if isHave {
			groupid = gconv.Int64(v["groupid"])
			break
		}
	}
	return groupid
}
